from binary_agent import binary_agent
from packetholder import packetholder
from constants import PCMD, PSIZE, PRIO, DCONSTS, SYSTEM
import random

class dmareq(object):
  req_type = PCMD.DMA_READ
  needed_lines = None  # remove from this list when lines actually read/written
  unhandled_lines = None # remove from this once request sent for this list
  requestor = None

class dmactrl(binary_agent):
  memtable = None

  active = True

  requests = None

  resolved_requests = 0

  def __init__(self, mtable):
    super(dmactrl, self).__init__()

    for holder in self.in_holders:
      holder.max_packets = DCONSTS.MAX_PACKETS
    for holder in self.out_holders:
      holder.max_packets = DCONSTS.MAX_PACKETS

    self.memtable = mtable

    self.requests = []

  def handle_packet(self, pkt):
    if(pkt.cmd in [PCMD.REP_UREAD, PCMD.REP_UWRITE]):
      for request in self.requests:
        if pkt.desc in request.needed_lines:
          request.needed_lines.remove(pkt.desc)
      return True

    elif(pkt.cmd is PCMD.DMA_READ):
      # do not handle request if no space for requests!
      if len(self.requests) >= DCONSTS.MAX_REQUESTS: return False

      new_req = dmareq()
      new_req.requestor = pkt.src
      new_req.req_type = PCMD.DMA_READ
      lines = range(pkt.desc, pkt.desc + pkt.extra)
      new_req.needed_lines = list(lines)
      new_req.unhandled_lines = list(lines)

      self.requests.append(new_req)

      return True
    
    elif(pkt.cmd is PCMD.DMA_WRITE):
      # do not handle request if no space for requests!
      if len(self.requests) >= DCONSTS.MAX_REQUESTS: return False

      new_req = dmareq()
      new_req.requestor = pkt.src
      new_req.req_type = PCMD.DMA_WRITE
      lines = range(pkt.desc, pkt.desc + pkt.extra)
      new_req.needed_lines = list(lines)
      new_req.unhandled_lines = list(lines)

      self.requests.append(new_req)

      return True

    else:
      raise NameError("DMA: Bad Packet CMD")
      return True

  def process(self):
    super(dmactrl, self).process()

    for request in list(self.requests):
      # issue data request packets for lines
      if request.unhandled_lines:
        for line in list(request.unhandled_lines):
          if request.req_type is PCMD.DMA_READ:
            new_pkt = self.issue_packet(PSIZE.REQ_UREAD, PRIO.REQ_UREAD)
            if new_pkt is None: break

            new_pkt.age = SYSTEM.TIME
            new_pkt.dest = self.memtable.lookup(line)
            new_pkt.cmd = PCMD.REQ_UREAD
            new_pkt.desc = line
            request.unhandled_lines.remove(line)

          else:
            new_pkt = self.issue_packet(PSIZE.REQ_UWRITE+SYSTEM.FLIT_P_LINE, PRIO.REQ_UWRITE)
            if new_pkt is None: break

            new_pkt.age = SYSTEM.TIME
            new_pkt.dest = self.memtable.lookup(line)
            new_pkt.cmd = PCMD.REQ_UWRITE
            new_pkt.desc = line
            request.unhandled_lines.remove(line)

      # check to see if to see if the request is fully handled
      elif not request.needed_lines:
        new_pkt = self.issue_packet(PSIZE.DMA_DONE, PRIO.DMA_DONE)
        if new_pkt is None: break

        new_pkt.age = SYSTEM.TIME
        new_pkt.dest = request.requestor
        new_pkt.cmd = PCMD.DMA_DONE

        self.resolved_requests += 1
        self.requests.remove(request)

  def to_string(self):
    return str(self.resolved_requests)
